/*
 * Created on Jun 10, 2005
 *
 */
package com.erclab.internal.xpresso.dataAccess;

import java.util.*;

import com.erclab.internal.xpresso.conf.Settings;
import com.erclab.internal.xpresso.datavalidators.DataValidationError;
import com.erclab.internal.xpresso.exceptions.*;
import com.erclab.internal.xpresso.formelements.FormElement;
import com.erclab.internal.xpresso.forms.Form;
import com.erclab.internal.xpresso.forms.FormParameters;
import com.erclab.internal.xpresso.reports.Report;
import com.erclab.internal.xpresso.security.User;
import com.erclab.internal.xpresso.util.ExceptionUtil;

/**
 * @author Enrique Rodriguez.
 *
 */
public class DataModifier {

	protected void sortByDestinationTable(List list){
	   Collections.sort(list, new Comparator() {
	   public int compare(Object o1, Object o2) {
	   		FormElement fe1 = (FormElement)o1; 
			FormElement fe2 = (FormElement)o2;
	   		String d1 = fe1.getDestinationTable();
	   		String d2 = fe2.getDestinationTable();
	   		return d1.compareTo(d2);
	   }
	   });
	   }

	public void executeFormChange(Form theForm, HashMap keyValues) throws DataModificationException, DataValidationError {
		FormParameters p = theForm.getFormParameters();
		if(p.getAction() == null){
			throw new DataModificationException("No action specified.");
		}
		
		// Verify that the user can use this form
		if (!Settings.getAccessController().isAllowedToUseResource(theForm, theForm.getFormParameters().getUser())){
			throw new SecurityException(ExceptionUtil.getSecurityExceptionString(theForm));
		}
		
		// Call the after submission method on the form helper
		if(theForm.getFormHelper() != null){
			try {
				theForm.getFormHelper().afterSubmission(theForm, keyValues);
			} catch (FormException e1) {
				throw new DataModificationException(e1);
			} 
		}
		
		executeChanges(theForm, keyValues, p);

		// Call the after modification method on the form helper
		if(theForm.getFormHelper() != null){
			try {
				theForm.getFormHelper().afterModification(theForm, keyValues);
			} catch (FormException e1) {
				throw new DataModificationException(e1);
			} 
		}
	}

	protected void executeChanges(
		Form theForm,
		HashMap keyValues,
		FormParameters p)
		throws DataModificationException {
		
		ArrayList formElements = theForm.getFormElements();
		sortByDestinationTable(formElements);
		boolean goNext = true;
		FormElement currentFe = null;
		
		for (Iterator iter = formElements.iterator(); iter.hasNext();) {
			if(goNext)
				currentFe = (FormElement)iter.next();
			FormElement previousFe = currentFe;
			ArrayList attributes = new ArrayList();
			int dataSourceId = 0;		
			boolean moreElements = true;
			do {
				dataSourceId = setFormElementsAttributes(theForm, keyValues, currentFe, attributes, dataSourceId);
				if(iter.hasNext()){
					currentFe = (FormElement)iter.next();
					goNext = false;
				} else {
					moreElements = false;
				}
			} while (currentFe.getDestinationTable().equalsIgnoreCase(previousFe.getDestinationTable()) 
						&& moreElements && !currentFe.isMultipleValue());
			
			ApplicationDataSource ads = Settings.getRepository().getApplicationDataSource(dataSourceId);
			
			if (attributes.size() > 0){
				try {
					if(p.getAction().equalsIgnoreCase("update")){
						if(previousFe.isMultipleValue()){
							ads.executeMultipleValueQuery(p.getAction(), attributes, previousFe.getDestinationTable(), theForm.getUniqueKeyName(), p.getUniqueKeyValue());
						} else {
							ads.executeUpdate(attributes, previousFe.getDestinationTable(), theForm.getUniqueKeyName(), p.getUniqueKeyValue());
						}
					} else if (p.getAction().equalsIgnoreCase("insert")){
						// For an insert we use the Parent Key Name to preserve the Parent
						// child relationship.
						if(previousFe.isMultipleValue()){
							ads.executeMultipleValueQuery(p.getAction(), attributes, previousFe.getDestinationTable(), theForm.getParentKeyName(), p.getUniqueKeyValue());
						} else {
							ads.executeInsert(attributes, previousFe.getDestinationTable(), theForm.getParentKeyName(), p.getUniqueKeyValue());
						}
					} else {
						throw new DataModificationException("Action '" + p.getAction() + "' not supported.");
					}
				} catch (DataSourceException e) {
					throw new DataModificationException("An error ocurred updating: " + e);
				}
			}
		}
	}

	protected int setFormElementsAttributes(
		Form theForm,
		HashMap keyValues,
		FormElement fe,
		ArrayList attributes,
		int dataSourceId) {
		if (fe.isModifiable()){
			Object o = keyValues.get("Field" + fe.getIdFormElement());
			Attribute a = new Attribute();
			a.setName(fe.getDestinationColumn());
			a.setValues(fe.processSubmittedValues(theForm, (String[])o));
			a.setDataType(fe.getDataType());
			attributes.add(a);
			if(dataSourceId == 0)
				dataSourceId = fe.getDestinationDataSourceId();

		}
		return dataSourceId;
	}

	/**
	 * @param theReport
	 * @param user
	 * @param elementsToDelete
	 */
	public void deleteElements(Report theReport, User user, String[] elementsToDelete) throws DataModificationException {
		
		ApplicationDataSource ads = Settings.getRepository().getApplicationDataSource(theReport.getIdDatasource());
		for(int i = 0 ; i < elementsToDelete.length; i++){
			try {
				ads.executeDelete(theReport.getTableAssociated(), theReport.getUniqueKeyName(), elementsToDelete[i]);
			} catch (DataSourceException e) {
				throw new DataModificationException("An error ocurred deleting: " + e);
			}
		}		
	}

}
